Skip to content

lighthouse: tree-shake bundled libs #6563

Open
FarhanAliRaza wants to merge 6 commits into
reflex-dev:mainfrom
FarhanAliRaza:lighthouse-pr4-pr5
Open

lighthouse: tree-shake bundled libs #6563
FarhanAliRaza wants to merge 6 commits into
reflex-dev:mainfrom
FarhanAliRaza:lighthouse-pr4-pr5

Conversation

@FarhanAliRaza

@FarhanAliRaza FarhanAliRaza commented May 25, 2026

Copy link
Copy Markdown
Contributor

Replace the blanket import * as of every bundled library with per-library named imports collected from the app's actual static usage (plus tags captured during dynamic-component serialization), so Rolldown can drop unused exports. Keep star imports for internal $/utils/* modules, react/@emotion/react, and any tag set that isn't a valid JS identifier.

Plumb theme_roots through the Radix Themes plugin and the Tailwind v3/v4 root style so only the Radix CSS token files for accent/gray colors referenced by Theme components are emitted, with the monolithic stylesheet retained as a fallback for state-driven or unrecognized colors.

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

fixes ENG-9323
image

Replace the blanket `import * as` of every bundled library with per-library
named imports collected from the app's actual static usage (plus tags
captured during dynamic-component serialization), so Rolldown can drop
unused exports. Keep star imports for internal `$/utils/*` modules,
`react`/`@emotion/react`, and any tag set that isn't a valid JS identifier.

Plumb `theme_roots` through the Radix Themes plugin and the Tailwind v3/v4
root style so only the Radix CSS token files for accent/gray colors
referenced by `Theme` components are emitted, with the monolithic
stylesheet retained as a fallback for state-driven or unrecognized colors.
@FarhanAliRaza FarhanAliRaza requested a review from a team as a code owner May 25, 2026 19:48
@codspeed-hq

codspeed-hq Bot commented May 25, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing FarhanAliRaza:lighthouse-pr4-pr5 (f697d5f) with main (8945367)2

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (92a7d36) during the generation of this report, so 8945367 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@greptile-apps

greptile-apps Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces blanket import * as star-imports for every bundled library with per-library named-import surfaces derived from the app's actual static usage (plus tags captured during dynamic-component serialization), enabling Rolldown to tree-shake unused exports from bundled libraries.

  • collect_window_library_imports (compiler.py) folds page and app-root import dicts plus a new dynamic_component_imports global into a {lib: set[tag] | None} map; react, @emotion/react, and internal $/… modules always get None (star import), while external libs get a named-export set.
  • _render_window_reflex_block (templates.py) emits either import { A as alias_A } from "lib" (for external libs with valid JS-identifier exports) or import * as alias from "lib" (fallback for invalid identifiers or internal/always-star libs), then constructs window["__reflex"] accordingly.
  • dynamic_component_imports (dynamic.py) is a new module-level dict populated during Component serialization so that eval'd dynamic components always find their required named exports on window.__reflex; it is reset at the start of each compile_app call.

Confidence Score: 4/5

The change is safe to merge with one edge case to keep in mind: an external bundled library that is never imported as a named export will be silently absent from window.__reflex, which could cause a runtime failure if a dynamically-eval'd component relies on it.

The core logic is correct and well-tested. The dynamic_component_imports mechanism correctly captures imports from serialized dynamic components before collect_window_library_imports runs. The react/@emotion/react always-star-import exemption preserves the window.__reflex.react access pattern expected by state.js. The main risk is the missing-library scenario where an external bundled library with no static named imports quietly disappears from window.__reflex — the if tags: guard silently drops it rather than falling back to a star import.

reflex/compiler/compiler.py — the collect_window_library_imports logic and its interaction with dynamic_component_imports; packages/reflex-base/src/reflex_base/compiler/templates.py — the _render_window_reflex_block output.

Important Files Changed

Filename Overview
reflex/compiler/compiler.py Adds collect_window_library_imports which builds a per-library named-export surface for window.__reflex; replaces blanket star-imports with scoped named imports for external bundled libs. Removes the old RADIX_THEMES_STYLESHEET constant and deduplicates the _get_all_imports tree walk by reusing app_root_imports.
packages/reflex-base/src/reflex_base/compiler/templates.py Adds _render_window_reflex_block which produces conditional named/star import lines and the useEffect body for window.__reflex; replaces the old unconditional star-import block. Contains a dead-code guard after the loop.
packages/reflex-base/src/reflex_base/components/dynamic.py Adds dynamic_component_imports module-level dict and reset_dynamic_component_imports to capture named imports from dynamically-serialized components; these are unioned into the window.__reflex surface by collect_window_library_imports.
tests/units/compiler/test_compiler.py Adds tests for collect_window_library_imports (internal star imports, external named imports, dynamic-component tag union, react always-star) and for the invalid-identifier star-import fallback; updates the existing radix window library test to use the new named-import assertions.

Reviews (3): Last reviewed commit: "refactor(reflex-base): clarify import-na..." | Re-trigger Greptile

Comment thread packages/reflex-components-radix/src/reflex_components_radix/plugin.py Outdated
When accent_color and its paired gray resolve to the same scale (e.g.
accent_color="gray"), the tokens CSS file was imported twice. Subtract
accents from grays before extending the sheet list so each color file
is emitted only once.
@FarhanAliRaza

Copy link
Copy Markdown
Contributor Author

Add more tests for dynamic components.

Drop the per-color-scale tree-shaking that emitted granular
@radix-ui/themes/tokens/colors/<color>.css imports and threaded
theme_roots through the compiler and plugin context. Restore the
single @radix-ui/themes/styles.css import, removing
get_radix_themes_stylesheets, strip_radix_theme_imports, and the
PreCompileContext.theme_roots field.
@FarhanAliRaza FarhanAliRaza changed the title lighthouse: tree-shake bundled libs and ship only used Radix color scales lighthouse: tree-shake bundled libs Jun 4, 2026
"""
from reflex.compiler.compiler import RADIX_THEMES_STYLESHEET
from reflex_components_radix.plugin import RADIX_THEMES_STYLESHEET

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we figure a way such that tailwind doesn't conceptually depend on radix themes?

component_imports = component._get_all_imports()
compiler._apply_common_imports(component_imports)

for lib, ivs in component_imports.items():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please avoid uncommon abbreviations like ivs in variable names

entries: list[str] = []
for lib, names in window_library_imports.items():
alias = f"__reflex_{_normalize_window_lib_alias(lib)}"
if names is None or any(not _JS_IDENTIFIER_RE.match(n) for n in names):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does any(not _JS_IDENTIFIER_RE.match(n) for n in names) mean? can we create a private helper function with a name for it?

Extract _all_valid_js_identifiers helper for the window-reflex import
check, and rename cryptic loop variables (ivs/iv -> import_vars/
import_var) in the dynamic component serializer. No behavior change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants